package com.hxk.demo.service.impl;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.github.rholder.retry.Retryer;
import com.github.rholder.retry.RetryerBuilder;
import com.github.rholder.retry.StopStrategies;
import com.github.rholder.retry.WaitStrategies;
import com.hxk.demo.constants.Constant;
import com.hxk.demo.dto.TongueCheckDTO;
import com.hxk.demo.service.TongueServiceI;
import com.hxk.demo.utils.Md5Util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * @author lyl
 */
@Slf4j
@Service
public class TongueServiceImpl implements TongueServiceI {

    @Resource
    private RestTemplate restTemplate;

    @Value("${channelId}")
    private String channelId;

    @Value("${secretKey}")
    private String secretKey;

    @Value("${baseUrl}")
    private String baseUrl;

    @Value("${accessTokenUrl}")
    private String accessTokenUrl;

    @Value("${checkTongueUrl}")
    private String checkTongueUrl;

    @Value("${tongueResultUrl}")
    private String tongueResultUrl;

    @Override
    public void getAccessToken() {
        Long timestamp = System.currentTimeMillis() / 1000;
        // 验证签名
        String sign = Md5Util.md5Encrypt32Lower(channelId + timestamp + secretKey);

        JSONObject paramObject = JSONUtil.createObj()
                .set("channelId", channelId)
                .set("timestamp", timestamp)
                .set("sign", sign);
        log.info("获取accessToken请求参数：{}", JSONUtil.toJsonStr(paramObject));
        JSONObject resObject = restTemplate.postForObject(baseUrl + accessTokenUrl, paramObject, JSONObject.class);
        log.info("获取accessToken返回结果：{}", JSONUtil.toJsonStr(resObject));

        if (null != resObject && resObject.getInt("code") == 0) {
            JSONObject data = resObject.getJSONObject("data");
            Constant.ACCESS_TOKEN = data.getStr("accessToken");
        }
    }

    @Override
    public JSONObject checkTongue(TongueCheckDTO dto) {
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "multipart/form-data");
        headers.add("accessToken", Constant.ACCESS_TOKEN);

        MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
        params.add("age", dto.getAge());
        params.add("sex", dto.getSex());
        try {
            ByteArrayResource byteArrayResource = new ByteArrayResource(dto.getTongueImage().getBytes()) {
                @Override
                public long contentLength() {
                    return dto.getTongueImage().getSize();
                }

                @Override
                public String getFilename() {
                    return dto.getTongueImage().getOriginalFilename();
                }
            };
            params.add("tongueImage", byteArrayResource);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("舌象诊断异常：{}", e.getMessage());
            return JSONUtil.createObj().set("ok", false).set("code", 4103).set("msg", "参数错误");
        }

        HttpEntity requestEntity = new HttpEntity<>(params, headers);
        JSONObject resObject = restTemplate.postForObject(baseUrl + checkTongueUrl, requestEntity, JSONObject.class);
        log.info("舌象诊断返回结果：{}", JSONUtil.toJsonStr(resObject));
        return resObject;
    }

    @Override
    public JSONObject tongueResult(String tongueId) {
        Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
                //设置根据结果重试
                .retryIfResult(res -> !res)
                //设置等待间隔时间
                .withWaitStrategy(WaitStrategies.fixedWait(1, TimeUnit.SECONDS))
                //设置最大重试次数
                .withStopStrategy(StopStrategies.stopAfterAttempt(10))
                .build();
        JSONObject returnObject = new JSONObject();
        try {
            retryer.call(() -> retryTask(tongueId, returnObject));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return returnObject;
    }

    public boolean retryTask(String tongueId, JSONObject returnObject) {
        JSONObject tongueCheckObject = new JSONObject();
        tongueCheckObject.set("tongueId", tongueId);

        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json");
        headers.add("accessToken", Constant.ACCESS_TOKEN);
        HttpEntity<JSONObject> requestEntity = new HttpEntity<>(tongueCheckObject, headers);
        JSONObject resObject = restTemplate.postForObject(baseUrl + tongueResultUrl, requestEntity, JSONObject.class);
        log.info("查询舌象诊断结果：{}", JSONUtil.toJsonStr(resObject));

        // code=300203：不是正确的舌象图片，请重新拍摄
        returnObject.putAll(resObject);
        if (null == resObject || (resObject.getInt("code") != 0 && resObject.getInt("code") != 300203)) {
            return false;
        }
        return true;
    }


}
